﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using CK.Sprite.Form.Core;
using Newtonsoft.Json.Linq;
using CK.Sprite.Framework;
using System.Web;
using System.IO;
using NPOI.HSSF.UserModel;
using CK.Sprite.Excel;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Data;
using CK.Sprite.ThirdContract;

namespace CK.Sprite.Form.Controllers
{
    [ApiController]
    [Area("spriteform")]
    [ControllerName("Runtime")]
    [Route("api/spriteform/common")]
    public class RuntimeController : Controller, ITransientDependency
    {
        private readonly IRuntimeAppService _commonAppService;
        private readonly IFormThirdServiceAppService _formThirdServiceAppService;
        protected ILogger<RuntimeController> Logger { get; }

        public RuntimeController(IRuntimeAppService commonAppService, IFormThirdServiceAppService formThirdServiceAppService, ILogger<RuntimeController> logger)
        {
            Logger = logger;
            _commonAppService = commonAppService;
            _formThirdServiceAppService = formThirdServiceAppService;
        }

        [HttpPost]
        [Route("DoRuntimeMethod")]
        public async Task<object> DoRuntimeMethod(object paramObject)
        {
            return await _formThirdServiceAppService.DoRuntimeMethod(paramObject as JObject);
        }

        [HttpPost]
        [Route("GetFormViewVueInfoByRelation")]
        public async Task<FormViewVueInfos> GetFormViewVueInfoByRelation(RelationInfoInputs relationInfoInputs)
        {
            return await _commonAppService.GetFormViewVueInfoByRelation(relationInfoInputs);
        }

        /// <summary>
        /// 获取字典信息
        /// </summary>
        /// <param name="version">字典版本号</param>
        /// <returns></returns>
        [HttpGet]
        [Route("GetDictInfos")]
        public async Task<DictWrap> GetDictInfos(string version)
        {
            return await _commonAppService.GetDictInfos(version);
        }

        [HttpGet]
        [Route("GetFormVueInfos")]
        public async Task<FormViewVueInfos> GetFormVueInfos(string applicationCode, Guid id, string dictVersion)
        {
            return await _commonAppService.GetFormVueInfos(applicationCode, id, dictVersion);
        }

        [HttpGet]
        [Route("GetViewVueInfos")]
        public async Task<FormViewVueInfos> GetViewVueInfos(string applicationCode, Guid id, string dictVersion)
        {
            return await _commonAppService.GetViewVueInfos(applicationCode, id, dictVersion);
        }

        [HttpGet]
        [Route("GetDictItemRemoteInfo")]
        public async Task<List<RemoteSourceDto>> GetDictItemRemoteInfo(string dictCode)
        {
            return await _commonAppService.GetDictItemRemoteInfo(dictCode);
        }

        #region 远程控件调用

        [HttpPost]
        [Route("DoGetRemoteSelectCall")]
        public async Task<List<ValueName>> DoGetRemoteSelectCall(DoGetRemoteSelectCallInput selectCallInput)
        {
            return await _commonAppService.DoGetRemoteSelectCall(selectCallInput);
        }

        [HttpPost]
        [Route("DoGetByIds")]
        public async Task<List<ValueName>> DoGetByIds(DoGetByIdsInput byIdsInput)
        {
            return await _commonAppService.DoGetByIds(byIdsInput);
        }

        [HttpPost]
        [Route("DoGetUserByIds")]
        public async Task<List<ValueName>> DoGetUserByIds(DoGetUserByIdsInput userByIdsInput)
        {
            return await _commonAppService.DoGetUserByIds(userByIdsInput);
        }

        #endregion

        #region Excel Operate

        /// <summary>
        /// 导入成员
        /// </summary>
        /// <param name="file">Excel文档</param>
        /// <param name="pId"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("ExcelImport")]
        public async Task<ActionResult> ExcelImport(IFormFile file)
        {
            var excelFiles = Request.Form.Files;
            var objectName = Request.Form["objectName"].ToString();
            var excelName = Request.Form["excelName"].ToString();
            var applicationCode = Request.Form["applicationCode"].ToString();
            if(string.IsNullOrEmpty(applicationCode) || applicationCode == "undefined")
            {
                applicationCode = "Default";
            }
            var strExcelTemplate = Request.Form["excelTemplate"].ToString();
            var strUniqKey = Request.Form["uniqKey"].ToString();
            var excelTemplates = JsonConvert.DeserializeObject<List<ExcelTemplate>>(strExcelTemplate);
            excelTemplates = excelTemplates.Where(r => r.TemplateForUse != ETemplateForUse.OnlyForExport).ToList();
            var strExcelDicts = Request.Form["excelDicts"].ToString();
            if (!string.IsNullOrEmpty(strExcelDicts))
            {
                var dictDbFrameworkCache = ServiceLocator.ServiceProvider.GetService<DictDbFrameworkCache>();
                var dictInfos = dictDbFrameworkCache.GetBusinessCacheDtos();
                var excelDicts = JsonConvert.DeserializeObject(strExcelDicts) as JArray;
                foreach (var excelDict in excelDicts)
                {
                    var dictInfo = dictInfos.FirstOrDefault(r => r.Id == excelDict["dict"].ToString());
                    var excelTemplate = excelTemplates.FirstOrDefault(r => excelDict["field"].ToString() == r.Field);
                    if (excelTemplate != null && dictInfo != null)
                    {
                        excelTemplate.DictionaryItems = new Dictionary<string, string>();
                        dictInfo.DictItemDtos.ForEach(r => excelTemplate.DictionaryItems.Add(r.Code, r.Name));
                    }
                }
            }

            var spriteObjectLocalCache = ServiceLocator.ServiceProvider.GetService<SpriteObjectLocalCache>();
            var spriteObjectDto = spriteObjectLocalCache.GetAll(applicationCode).FirstOrDefault(r => r.Name == objectName);
            if (spriteObjectDto == null)
            {
                return await MakeImportCommonErrorAsync("未找到Object实体信息");
            }

            JObject objSameParams = null;
            List<string> strValues = new List<string>();
            if (!string.IsNullOrEmpty(strUniqKey))
            {
                if (spriteObjectDto.UnionIndex != strUniqKey && !spriteObjectDto.ObjectPropertyDtos.Exists(r => r.IsUnique && r.Name == strUniqKey))
                {
                    return await MakeImportCommonErrorAsync("导入数据的唯一字段必须是唯一字段或者联合唯一字段");
                }

                var originImportInfo = ExcelHttpHelper.GetOriginUploadInfo(excelFiles, excelTemplates);
                if (!originImportInfo.IsSuccess)
                {
                    return await MakeImportCommonErrorAsync(originImportInfo.ResultMsg);
                }

                // 获取数据库重复字段信息
                if (spriteObjectDto.UnionIndex == strUniqKey)
                {
                    var splitUnionIndexs = spriteObjectDto.UnionIndex.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    Dictionary<string, ObjectPropertyDto> dictFindProperties = new Dictionary<string, ObjectPropertyDto>();
                    foreach (var splitUnionIndex in splitUnionIndexs)
                    {
                        var findProperty = spriteObjectDto.ObjectPropertyDtos.FirstOrDefault(r => r.Name.ToLower() == splitUnionIndex.ToLower());
                        if (findProperty == null)
                        {
                            return await MakeImportCommonErrorAsync($"联合唯一字段[{splitUnionIndex}]未定义");
                        }
                        dictFindProperties.Add(splitUnionIndex, findProperty);
                    }
                    foreach (DataRow row in originImportInfo.OriginTable.Rows)
                    {
                        List<string> strConcat = new List<string>();
                        foreach (var splitUnionIndex in splitUnionIndexs)
                        {
                            if (row[splitUnionIndex.ToCamelCase()] != null)
                            {
                                MakeImportSameValueAsync(row, splitUnionIndex.ToCamelCase(), dictFindProperties[splitUnionIndex], strConcat);
                            }
                        }

                        strValues.Add(string.Join("@#$", strConcat));
                    }
                }
                else
                {
                    var findProperty = spriteObjectDto.ObjectPropertyDtos.FirstOrDefault(r => r.Name.ToLower() == strUniqKey.ToLower());
                    foreach (DataRow row in originImportInfo.OriginTable.Rows)
                    {
                        MakeImportSameValueAsync(row, strUniqKey.ToCamelCase(), findProperty, strValues);
                    }
                }
                if (strValues.Count != strValues.Distinct().Count())
                {
                    return await MakeImportCommonErrorAsync($"Excel表中存在重复记录，请先检查！");
                }
                objSameParams = await _commonAppService.DoGetUniqInfos(applicationCode, spriteObjectDto, strUniqKey, strValues);
            }

            ImportParamInfo importParamInfo = new ImportParamInfo()
            {
                SpriteObjectDto = spriteObjectDto,
                DbSameInfos = objSameParams,
                UniqKey = strUniqKey
            };

            var importResultInfo = ExcelHttpHelper.GetUploadInfos(excelFiles, excelTemplates, ValidateExcelColum, null, true, importParamInfo);

            return await HandleImportResultAsync(importResultInfo, $"{DateTime.Now.ToString("yyyyMMddHHmm")}导入{excelName}数据错误", async (importResult) =>
            {
                foreach (DataColumn tempColumn in importResult.UpdateTable.Columns)
                {
                    if(tempColumn.ColumnName == "SameId")
                    {
                        tempColumn.ColumnName = "Id";
                    }
                }
                await _commonAppService.ImportExcel(applicationCode, ExcelHelper.ConvertToJArray(importResult.SuccessTable), ExcelHelper.ConvertToJArray(importResult.UpdateTable), spriteObjectDto);
            });
        }

        private async Task<ActionResult> MakeImportCommonErrorAsync(string errorMsg)
        {
            return await HandleImportResultAsync(new ImportResult()
            {
                ImportResultMsg = errorMsg,
                IsSuccess = false
            }, "", null);
        }

        private string MakeImportSameValueAsync(DataRow row, string strUniqKey, ObjectPropertyDto findProperty, List<string> strValues)
        {
            var strError = "";
            if (row[strUniqKey] == null)
            {
                strError = $"唯一字段[{findProperty.Description}]不能为空";
            }
            try
            {
                if (findProperty.FieldType == Core.EFieldType.Date || findProperty.FieldType == Core.EFieldType.DateTime)
                {
                    strValues.Add(Convert.ToDateTime(row[strUniqKey]).ToString("yyyy-MM-dd_HH:mm:ss"));
                }
                else
                {
                    if (findProperty.FieldType == Core.EFieldType.Bool)
                    {
                        strValues.Add(Convert.ToInt32(row[strUniqKey]).ToString());
                    }
                    else
                    {
                        strValues.Add(row[strUniqKey].ToString());
                    }
                }
            }
            catch (Exception ex)
            {
                strError = $"唯一字段[{findProperty.Description}]格式填写错误";
            }

            return strError;
        }

        private CustomerValidateResult ValidateExcelColum(DataRow validateRow, DataTable importTable, DataTable tempSuccessTable, Object objParam)
        {
            var importParamInfo = objParam as ImportParamInfo;
            if (string.IsNullOrEmpty(importParamInfo.UniqKey))
            {
                return new CustomerValidateResult()
                {
                    IsSuccess = true,
                    IsSame = false
                };
            }
            else
            {
                var strValue = "";
                if (importParamInfo.SpriteObjectDto.UnionIndex == importParamInfo.UniqKey)
                {
                    var splitUnionIndexs = importParamInfo.SpriteObjectDto.UnionIndex.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    List<string> strConcat = new List<string>();
                    foreach (var splitUnionIndex in splitUnionIndexs)
                    {
                        var findProperty = importParamInfo.SpriteObjectDto.ObjectPropertyDtos.FirstOrDefault(r => r.Name == splitUnionIndex);
                        if (validateRow[findProperty.Description] != null)
                        {
                            MakeImportSameValueAsync(validateRow, findProperty.Description, findProperty, strConcat);
                        }
                    }

                    strValue = string.Join("@#$", strConcat);
                }
                else
                {
                    var findProperty = importParamInfo.SpriteObjectDto.ObjectPropertyDtos.FirstOrDefault(r => r.Name.ToLower() == importParamInfo.UniqKey.ToLower());
                    strValue = validateRow[findProperty.Description].ToString();
                }
                var arraySames = importParamInfo.DbSameInfos["result"] as JArray;
                if (arraySames.Count > 0)
                {
                    var findDbValue = arraySames.FirstOrDefault(r => r["UniqData"].ToString() == strValue);
                    if (findDbValue != null)
                    {
                        return new CustomerValidateResult()
                        {
                            IsSuccess = true,
                            IsSame = true,
                            SameId = findDbValue["Id"].ToString()
                        };
                    }
                    else
                    {
                        return new CustomerValidateResult()
                        {
                            IsSuccess = true,
                            IsSame = false
                        };
                    }
                }
                return new CustomerValidateResult()
                {
                    IsSuccess = true,
                    IsSame = false
                };
            }
        }

        /// <summary>
        /// 下载模板
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("DownExcelTemplateAsync")]
        public async Task<object> DownExcelTemplateAsync(object paramObject)
        {
            var objParamObject = paramObject as JObject;
            var methods = objParamObject["methods"].Children();
            JToken excelMethod = null;
            foreach (var method in methods)
            {
                if ((method["datas"] as JObject).ContainsKey("excelTemplate"))
                {
                    excelMethod = method;
                }
            }
            if (excelMethod == null)
            {
                return null;
            }

            var excelTemplates = excelMethod["datas"]["excelTemplate"].ToObject<List<ExcelTemplate>>();
            excelTemplates = excelTemplates.Where(r => r.TemplateForUse != ETemplateForUse.OnlyForExport).ToList();
            if ((excelMethod["datas"] as JObject).ContainsKey("excelDicts"))
            {
                var dictDbFrameworkCache = ServiceLocator.ServiceProvider.GetService<DictDbFrameworkCache>();
                var dictInfos = dictDbFrameworkCache.GetBusinessCacheDtos();
                var excelDicts = excelMethod["datas"]["excelDicts"] as JArray;
                foreach (var excelDict in excelDicts)
                {
                    var dictInfo = dictInfos.FirstOrDefault(r => r.Id == excelDict["dict"].ToString());
                    var excelTemplate = excelTemplates.FirstOrDefault(r => excelDict["field"].ToString() == r.Field);
                    if (excelTemplate != null && dictInfo != null)
                    {
                        excelTemplate.DictionaryItems = new Dictionary<string, string>();
                        dictInfo.DictItemDtos.ForEach(r => excelTemplate.DictionaryItems.Add(r.Code, r.Name));
                    }
                }
            }
            var exportResult = ExcelHelper.ExportTemplate(excelTemplates, $"{excelMethod["datas"]["excelName"]}");

            return ExportResult(exportResult, $"{excelMethod["datas"]["excelName"]}模版.xls");
        }

        [HttpPost]
        [Route("ExportExcelAsync")]
        public async Task<object> ExportExcelAsync(object paramObject)
        {
            var objParamObject = paramObject as JObject;
            var methods = objParamObject["methods"].Children();
            JToken excelMethod = null;
            foreach (var method in methods)
            {
                if ((method["datas"] as JObject).ContainsKey("excelTemplate"))
                {
                    excelMethod = method;
                }
            }
            if (excelMethod == null)
            {
                return null;
            }
            var results = (await _formThirdServiceAppService.DoRuntimeMethod(paramObject as JObject)) as JArray;
            JArray excelResult = null;
            foreach (var result in results)
            {
                if (result["ruleId"].ToString() == excelMethod["ruleId"].ToString())
                {
                    excelResult = result["result"] as JArray;
                }
            }
            if (excelResult == null)
            {
                return null;
            }
            var excelTemplates = excelMethod["datas"]["excelTemplate"].ToObject<List<ExcelTemplate>>();
            excelTemplates = excelTemplates.Where(r => r.TemplateForUse != ETemplateForUse.OnlyForImport).ToList();
            if ((excelMethod["datas"] as JObject).ContainsKey("excelDicts"))
            {
                var dictDbFrameworkCache = ServiceLocator.ServiceProvider.GetService<DictDbFrameworkCache>();
                var dictInfos = dictDbFrameworkCache.GetBusinessCacheDtos();
                var excelDicts = excelMethod["datas"]["excelDicts"] as JArray;
                foreach (var excelDict in excelDicts)
                {
                    var dictInfo = dictInfos.FirstOrDefault(r => r.Id == excelDict["dict"].ToString());
                    var excelTemplate = excelTemplates.FirstOrDefault(r => excelDict["field"].ToString() == r.Field);
                    if (excelTemplate != null && dictInfo != null)
                    {
                        excelTemplate.DictionaryItems = new Dictionary<string, string>();
                        dictInfo.DictItemDtos.ForEach(r => excelTemplate.DictionaryItems.Add(r.Code, r.Name));
                    }
                }
            }
            var exportResult = ExcelHelper.Export(excelResult, excelTemplates, $"{excelMethod["datas"]["excelName"]}");

            return ExportResult(exportResult, $"{excelMethod["datas"]["excelName"]}({DateTime.Now.ToString("yyyy-MM-dd HH:mm")}).xls");
        }

        private async Task<ActionResult> HandleImportResultAsync(ImportResult importResultInfo, string errorName, Func<ImportResult, Task> action)
        {
            if (importResultInfo.IsSuccess)
            {
                try
                {
                    await action?.Invoke(importResultInfo);
                }
                catch (Exception ex)
                {
                    Response.Headers.Add("ImportResult", HttpUtility.UrlEncode(ex.Message));
                    Response.Headers.Add("HaveErrorTable", "false");
                    if (ex is SpriteException)
                    {

                    }
                    else
                    {
                        Logger.LogError(ex, ex.Message);
                    }
                    return Content(ex.Message);
                }

                Response.Headers.Add("ImportResult", HttpUtility.UrlEncode(importResultInfo.ImportResultMsg));
                Response.Headers.Add("HaveErrorTable", "false");
                return Content(importResultInfo.ImportResultMsg);
            }
            else
            {
                if (importResultInfo.ErrorTable == null)
                {
                    Response.Headers.Add("ImportResult", HttpUtility.UrlEncode(importResultInfo.ImportResultMsg));
                    Response.Headers.Add("HaveErrorTable", "false");
                    return Content(importResultInfo.ImportResultMsg);
                }
                else
                {
                    try
                    {
                        await action?.Invoke(importResultInfo);
                    }
                    catch (Exception ex)
                    {
                        Response.Headers.Add("ImportResult", HttpUtility.UrlEncode(ex.Message));
                        Response.Headers.Add("HaveErrorTable", "false");
                        if (ex is SpriteException)
                        {

                        }
                        else
                        {
                            Logger.LogError(ex, ex.Message);
                        }
                        return Content(ex.Message);
                    }

                    var exportResult = ExcelHelper.ExportFromTable(importResultInfo.ErrorTable, errorName);
                    Response.Headers.Add("ImportResult", HttpUtility.UrlEncode(importResultInfo.ImportResultMsg));
                    Response.Headers.Add("HaveErrorTable", "true");
                    Response.Headers.Add("ErrorTableName", HttpUtility.UrlEncode($"{errorName}.xls"));

                    return ExportResult(exportResult, $"{errorName}.xls");
                }
            }
        }
        private ActionResult ExportResult(HSSFWorkbook exportResult, string fileName)
        {
            byte[] buffer;
            using (MemoryStream ms = new MemoryStream())
            {
                exportResult.Write(ms);
                buffer = ms.ToArray();
                ms.Close();
            }

            return File(
                fileContents: buffer,
                contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                fileDownloadName: fileName
            );
        }

        #endregion
    }

    internal class ImportParamInfo
    {
        public SpriteObjectDto SpriteObjectDto { get; set; }
        public JObject DbSameInfos { get; set; }
        public string UniqKey { get; set; }
    }
}
